Skip to content

fix(auth): log in when the browser is not on this machine - #1418

Draft
cristifalcas wants to merge 3 commits into
mainfrom
cf/auth-login-without-a-local-browser
Draft

fix(auth): log in when the browser is not on this machine#1418
cristifalcas wants to merge 3 commits into
mainfrom
cf/auth-login-without-a-local-browser

Conversation

@cristifalcas

@cristifalcas cristifalcas commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Fixes aspect auth login when the CLI and browser run on different machines. Closes ENG-2098.

What changed

  • Open the browser with the platform defaults for Linux, macOS, and Windows, probing launchers without waiting for the browser process to exit.
  • Detect SSH sessions without X11 forwarding and fall back to a manual callback flow.
  • Add --no-browser to aspect auth login and aspect auth configure. The explicit flag reads the callback from stdin even when stdin is not a TTY.
  • Let an auth session finish from either its loopback listener or a pasted callback URL or authorization code.
  • Share callback parsing, token exchange, and credential creation between both completion paths.

Behaviour

  • A successfully launched local browser uses the loopback listener.
  • SSH without forwarded X11, launcher failure, or --no-browser prints the authorization URL and prompts for the callback URL or code when input is interactive or explicitly requested.
  • An automatic non-interactive fallback keeps waiting on the listener, allowing an already-forwarded callback to arrive.

For self-hosted deployments, listener callbacks require the expected OAuth state. Pasted callback URLs validate state; a bare authorization code may omit it and remains bound to the login session by PKCE.

Test plan

  • bazel test //crates/axl-runtime:test //crates/aspect-cli:test
  • bazel-bin/crates/aspect-cli/aspect-cli tests axl — 991 tests passed
  • Unit coverage for callback parsing, callback-state policy, one-shot pasted-code exchange, SSH/X11 detection, platform launcher selection, launcher fallback, and foreground launcher processes
  • Manual coverage for local login, SSH with DISPLAY=:0, SSH with forwarded X11, --no-browser, loopback delivery, stale callback state, missing codes, and empty input

Not yet verified: a live end-to-end login with a real authorization code, and the Linux fallback launcher table on Linux.

`aspect auth login` opened the browser by shelling out to `open`, then waited
for the OAuth code on a loopback listener. Both halves fail away from a desktop:

  - `open` is macOS. On Linux it is at best Debian's xdg-utils symlink, so a
    login either recited `www-browser: not found` six times or died with
    "failed to spawn command open" and an AXL traceback (ENG-2098).
  - Worse, the redirect is a loopback address. Paste the URL into a browser on
    another machine and the code is delivered to *that* machine's port —
    ERR_CONNECTION_REFUSED for the user and a CLI that waits forever. No amount
    of browser-opening fixes it; over SSH the code has to come back by hand.

Opening a browser moves into the runtime as `ctx.aspect.auth.open_browser`,
which carries the platform table (`$BROWSER`, then xdg-open/gio/gnome-open/
kde-open/x-www-browser/wslview on Linux, `open` on macOS, rundll32 on Windows —
it takes the URL as one argument, where `cmd /c start` would hand an
`&`-separated authorize URL back to cmd.exe to re-parse). Launcher output is
discarded; the task prints one message that says what to do instead.

It reports "headless" without trying at all when this is an SSH session with no
forwarded display. A display counts only if it names a host (`localhost:10.0`,
what ssh -X sets) — a bare `:0` is the remote machine's own screen, which is
what a Cloud Workstation image sets and where a browser would open in front of
nobody.

`AuthSession.redeem(pasted)` is then the login path for those sessions: it takes
the browser's full address-bar URL, a bare query string, or the bare code.
`wait()` and `redeem()` now share one `complete()`, so both reach an identical
token exchange; `state` is still validated whenever it is present, and a stale
paste from an earlier run is rejected. Off a TTY there is nothing to paste with,
so the listener is still waited on — a forwarded port delivers.

`--no-browser` forces the paste flow on both `login` and `configure`. The port
is only worth forwarding for the Aspect account, which binds a fixed 19556; a
self-hosted deployment takes a fresh port each run, so that caveat lives in the
flag's help text rather than as a hint the user cannot act on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 44fc76ba-041f-46aa-b43e-3f076af7ad15

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cristifalcas
cristifalcas marked this pull request as draft September 1, 2026 14:01

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ef89800e5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1628 to +1629
for entry in browser_env.unwrap_or_default().split(':') {
let argv: Vec<String> = entry.split_whitespace().map(str::to_string).collect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve quoting when parsing BROWSER commands

When $BROWSER contains a quoted command line such as firefox --new-window "%s" or sh -c 'open-browser %s', split_whitespace() leaves the quote characters in the arguments and splits shell expressions incorrectly. spawn_launcher then substitutes the authorize URL into those malformed arguments; if the launcher still exits successfully, the CLI reports that the browser opened and waits indefinitely even though it received an invalid URL. Parse each colon-separated entry with shell-aware argument parsing rather than raw whitespace splitting.

Useful? React with 👍 / 👎.

@aspect-workflows

aspect-workflows Bot commented Sep 1, 2026

Copy link
Copy Markdown

Aspect Workflows Tasks

📅 Thu Sep 3 19:47:31 UTC 2026

Task Results


⏱ Last updated Thu Sep 3 19:58:09 UTC 2026 · 📊 GitHub API quota 2,142/15,000 (14% used, resets in 23m)
🚀 Powered by Aspect CLI (v0.0.0-dev)  |  Aspect Build · X · LinkedIn · YouTube

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant